#include "pch.h"
#include "work.h"

void application::start()
{
	view v;
	v.codes = analy.codes;
	work(v);
}

void application::init_view(view& v)
{
	if (current_view.c.tokens.size() > 0)
	{
		views.push(current_view);
	}
	current_view = v;
	
}

void application::remove_view(view& v)
{
	v = current_view;
	if (!views.empty())
	{
		current_view = views.top();
		views.pop();
	}
	else
	{
		current_view.c.tokens.clear();
		current_view.pos = 0;
	}
}

void application::work(view& v)
{
	init_view(v);
	while (current_view.next_code())
	{
		if (current_view.c.tokens.size() <= 0) continue;
		w_code();
	}
	remove_view(v);
}

void application::w_code()
{
	Token t = current_view.get_token();
	switch (t.type)
	{
	case WORD_TOKEN:
	{
		current_view.back();
		switch (t.second_type)
		{
		case DESCRIBE_WORD_TOKEN:
		case OBJECT_WORD_TOKEN:
			w_object();
			break;
		case METHOD_WORD_TOKEN:
			w_method();
			break;
		case VAR_WORD_TOKEN:
		{
			t = current_view.get_token();
			Token s = current_view.get_token();
			if (s.type != KEYWORD_TOKEN || s.wchar != ASG_TOKEN)
				throw Error("Bad expression", __FILE__, __LINE__);
			Data d = w_describe();
			if (views.empty())
				datas[t.word] = d;
			else
				current_view.datas[t.word] = d;
			break;
		}
		}
		break;
	}
	case KEYWORD_TOKEN:
		switch (t.wchar)
		{
		case TALK_TOKEN:
		{
			t = current_view.get_token();
			if (t.type == SYMBAL_TOKEN && t.wchar == END_TOKEN)
			{
				break;
			}
			current_view.back();
			while (true)
			{
				Data d = w_describe();
				out << d << " ";
				t = current_view.get_token();
				if (t.type == SYMBAL_TOKEN && t.wchar == END_TOKEN)
				{
					break;
				}
				else if (t.type != SYMBAL_TOKEN || t.wchar != NEXT_TOKEN)
				{
					throw Error("Bad Expression", __FILE__, __LINE__);
				}
			}
			out << std::endl;
			break;
		}
		}
		break;
	default:
		throw Error("Bad Expression", __FILE__, __LINE__);
	}
}

void application::w_object()
{
	Token pre = current_view.get_token();
	if (pre.type != WORD_TOKEN && pre.second_type != VAR_WORD_TOKEN)
		throw Error("Bad expression", __FILE__, __LINE__);
	Token t = current_view.get_token();
	switch (t.type)
	{
	case WORD_TOKEN:
	{
		Data left = get_data(pre.word);
		if (t.second_type != METHOD_WORD_TOKEN)
			throw Error("Bad expression", __FILE__, __LINE__);
		obj_method_obj(left);
		break;
	}
	case KEYWORD_TOKEN:
	{
		if (t.wchar == ASG_TOKEN)
		{
			Data v = w_describe();
			if (views.empty())
			{
				datas[pre.word] = v;
			}
		}
		else
		{
			throw Error("Bad expression", __FILE__, __LINE__);
		}
	}
	default:
		throw Error("Bad expression", __FILE__, __LINE__);
	}
}

void application::w_method()
{
	Token t = current_view.get_token();
	method_info m_info = get_method(t.word);
	view v;
	v.init(m_info);
	t = current_view.get_token();
	if (t.type == SYMBAL_TOKEN)
		current_view.back();
	else if (t.type == WORD_TOKEN && t.second_type == VAR_WORD_TOKEN)
	{
		Data d = w_describe();
		v.init(d);
	}
	else
		throw Error("Bad Expression", __FILE__, __LINE__);
	work(v);
}

Data application::w_describe()
{
	Token t;
	std::stack<std::wstring> describes;
	while (true)
	{
		t = current_view.get_token();
		switch (t.type)
		{
		case WORD_TOKEN:
		{
			if (t.second_type == VAR_WORD_TOKEN)
			{
				Data d = get_data(t.word);
				if (d.type == STRING_DATA || d.type == NUMBER_DATA)
					return d;
				while (!describes.empty())
				{
					std::wstring n = describes.top();
					describes.pop();
					describe_info d_info = get_describe(n, d.v_object.type);

					view v;
					v.init(d_info, d);
					work(v);

					d = v.get_target();
				}
				return d;
			}
			else if (t.second_type == OBJECT_WORD_TOKEN)
			{
				object_info o_info = get_object_info(t.word);
				Data d = get_data(o_info);
				if (d.type == STRING_DATA || d.type == NUMBER_DATA)
					throw Error("Describe can't describe number or string", __FILE__, __LINE__);
				while (!describes.empty())
				{
					std::wstring n = describes.top();
					describes.pop();
					describe_info d_info = get_describe(n, d.v_object.type);

					view v;
					v.init(d_info, d);
					work(v);

					d = v.get_target();
				}
				return d;
			}
			else if (t.second_type == DESCRIBE_WORD_TOKEN)
			{
				describes.push(t.word);
				t = current_view.get_token();
				if (t.type != KEYWORD_TOKEN || t.wchar != DOT_TOKEN)
					current_view.back();
			}
			break;
		}
		case NUMBER_TOKEN:
		case STRING_TOKEN:
			current_view.back();
			return get_data();
		default:
			throw Error("Bad Expression", __FILE__, __LINE__);
		}
	}
	return none_data;
}

void application::obj_method_obj(Data& self)
{
	if (self.type == STRING_DATA || self.type == NUMBER_DATA)
		throw Error("Type error", __FILE__, __LINE__);
	Token t = current_view.get_token();
	if (t.type != WORD_TOKEN || t.second_type != METHOD_WORD_TOKEN)
		throw Error("Bad expression", __FILE__, __LINE__);
	method_info m_info = none_method_info;
	for (auto i : self.v_object.type.methods)
	{
		if (i.name == t.word)
		{
			m_info = i;
			break;
		}
	}
	if (m_info == none_method_info)
		throw Error("Method is not define", __FILE__, __LINE__);
	view v;
	v.init(self);
	t = current_view.get_token();
	if (t.type == WORD_TOKEN)
	{
		if (t.second_type == VAR_WORD_TOKEN || t.second_type == OBJECT_WORD_TOKEN || t.type == DESCRIBE_WORD_TOKEN)
		{
			Data r = w_describe();
			v.init(r);
		}
		else
			current_view.back();
	}
	else
		current_view.back();
	v.init(m_info);
	work(v);
}

void application::talk()
{
	Token t = current_view.get_token();
	while (true)
	{
		if (t.type == SYMBAL_TOKEN && t.wchar == END_TOKEN)
			break;
		else if(t.type == SYMBAL_TOKEN)
			current_view.back();

		Data d = get_data();
		out << d << " ";
	}
}

Data application::get_data()
{
	Data ret;
	Token t = current_view.get_token();
	switch (t.type)
	{
	case STRING_TOKEN:
	{
		ret.type = STRING_DATA;
		ret.v_str = t.word;
		break;
	case NUMBER_TOKEN:
		ret.type = NUMBER_DATA;
		ret.v_num = t.number;
		break;
	case WORD_TOKEN:
		switch (t.second_type)
		{
		case METHOD_WORD_TOKEN:
		case DESCRIBE_WORD_TOKEN:
		case OBJECT_WORD_TOKEN:
		{
			ret.type = STRING_DATA;
			ret.v_str = t.word;
			break;
		}
		case VAR_WORD_TOKEN:
			ret = get_data(t.word);
			break;
		default:
			throw Error("Bad Expression", __FILE__, __LINE__);
		}
		break;
	default:
		throw Error("Bad Expression", __FILE__, __LINE__);
	}
	}
	return ret;
}

Data application::get_data(std::wstring n)
{
	if (current_view.datas.count(n) > 0) return current_view.datas[n];
	if (datas.count(n) > 0) return datas[n];
	return none_data;
}

Data application::get_data(object_info o_info)
{
	Data ret;
	ret.type = OBJECT_DATA;
	ret.v_object = object(o_info);
	return ret;
}

method_info application::get_method(std::wstring n)
{
	method_info m_info;
	for (auto i : analy.methods)
	{
		if (i.name == n)
		{
			return i;
		}
	}
	for (auto i : current_view.methods)
	{
		if (i.name == n)
		{
			return i;
		}
	}
	return none_method_info;
}

describe_info application::get_describe(std::wstring n, object_info tag)
{
	for (auto i : analy.describes)
	{
		if (i.name == n && i.target == tag)
			return i;
	}
	return none_describe_info;
}

object_info application::get_object_info(std::wstring n)
{
	for (auto i : analy.types)
	{
		if (i.name == n)
			return i;
	}
	return none_type_info;
}

void view::init(object_info o_info)
{
	for (argument arg : o_info.properties)
	{
		datas[arg.name] = arg.default_data;
	}
	methods = o_info.methods;
}

void view::init(Data d)
{
	for (auto i : d.v_object.vars)
	{
		datas.insert(i);
	}
}

void view::init(method_info m_info)
{
	for (argument arg : m_info.args)
	{
		datas[arg.name] = arg.default_data;
	}
	codes = m_info.body;
}

void view::init(describe_info d_info, Data& tag)
{
	for (argument arg : d_info.args)
	{
		datas[arg.name] = arg.default_data;
	}
	init(tag);
	target = tag;
	codes = d_info.body;
}



Token view::get_token()
{
	if (pos >= 0 && pos < c.tokens.size())
	{
		return c.tokens[pos++];
	}
	return none_token;
}

void view::back(size_t i)
{
	pos -= i;
}

void view::set_code(code _c)
{
	c = _c;
	pos = 0;
}

bool view::next_code()
{
	if (code_pos >= 0 && code_pos < codes.size())
	{
		c = codes[code_pos++];
		pos = 0;
		return true;
	}
	return false;
}

Data view::get_target()
{
	for (auto i : target.v_object.vars)
	{
		target.v_object.vars[i.first] = datas[i.first];
	}
	return target;
}
